﻿#pragma once

#include "MapObject.h"
#include "../../Shared/Enums.h"
#include "../MirDatabase/MonsterInfo.h"
#include "../MirEnvir/Map.h"
#include "../MirDatabase/RespawnInfo.h"
#include "../../Shared/Data/ItemData.h"
#include "DelayedAction.h"
#include "PlayerObject.h"
#include "../../Shared/Data/Stat.h"
#include "../../Shared/Packet.h"
#include <string>
#include <vector>
#include <algorithm>
#include <cmath>
#include <limits>
#include <any>
#include "exceptionhelper.h"
#include "../../stringhelper.h"

using namespace Server::MirDatabase;
using namespace Server::MirEnvir;
using namespace Server::MirObjects::Monsters;
namespace S = ServerPackets;

namespace Server::MirObjects
{
    class MonsterObject : public MapObject
    {
    private:
        int CurrentMapIndex = 0;
        Point *CurrentLocation;
        MirDirection Direction = static_cast<MirDirection>(0);

    public:
        virtual ~MonsterObject()
        {
            delete Info;
            delete Respawn;
        }

        static MonsterObject *GetMonster(MonsterInfo *info);

        ObjectType getRace() const override;

        MonsterInfo *Info;
        MapRespawn *Respawn;

        std::string getName() const override;
        void setName(const std::string &value) override;

        int getCurrentMapIndex() const override;
        void setCurrentMapIndex(int value) override;
        Point *getCurrentLocation() const override;
        void setCurrentLocation(Point *value) override;
        MirDirection getDirection() const final override;
        void setDirection(MirDirection value) final override;
        unsigned short getLevel() const override;
        void setLevel(unsigned short value) override;

        AttackMode getAMode() const final override;
        void setAMode(AttackMode value) final override;
        PetMode getPMode() const final override;
        void setPMode(PetMode value) final override;

        int getHealth() const override;

        int getMaxHealth() const override;

        int HP = 0;

        unsigned short MoveSpeed = 0;

        virtual unsigned int getExperience() const;
        int getDeadDelay() const;
        static constexpr int RegenDelay = 10000, EXPOwnerDelay = 5000, AloneDelay = 3000, SearchDelay = 3000, RoamDelay = 1000, HealDelay = 600, RevivalDelay = 2000;
        long long ActionTime = 0, MoveTime = 0, AttackTime = 0, RegenTime = 0, DeadTime = 0, AloneTime = 0, SearchTime = 0, RoamTime = 0, HealTime = 0;
        long long ShockTime = 0, RageTime = 0, HallucinationTime = 0;
        bool BindingShotCenter = false, PoisonStopRegen = true;

    protected:
        bool Alone = false, Stacking = false;

    public:
        unsigned char PetLevel = 0;
        unsigned int PetExperience = 0;
        unsigned char MaxPetLevel = 0;
        long long TameTime = 0;

        int RoutePoint = 0;
        bool Waiting = false;

        std::vector<MonsterObject*> SlaveList = std::vector<MonsterObject*>();
        std::vector<RouteInfo*> Route = std::vector<RouteInfo*>();

        bool getBlocking() const override;
    protected:
        virtual bool getCanRegen() const;
        virtual bool getCanMove() const;
        virtual bool getCanAttack() const;

        MonsterObject(MonsterInfo *info);
    public:
        bool Spawn(Map *temp, Point *location);
        bool Spawn(MapRespawn *respawn);

        void Spawned() override;

    protected:
        virtual void RefreshBase();

    public:
        virtual void RefreshAll();

    protected:
        virtual void RefreshBuffs();
    public:
        void RefreshNameColour(bool send = true);

        void SetHP(int amount);
        virtual void ChangeHP(int amount);

        //use this so you can have mobs take no/reduced poison damage
        virtual void PoisonDamage(int amount, MapObject *Attacker);


        bool Teleport(Map *temp, Point *location, bool effects = true, unsigned char effectnumber = 0) override;


        void Die() override;

        void Revive(int hp, bool effect);

        int Pushed(MapObject *pusher, MirDirection dir, int distance) override;

    protected:
        virtual void Drop();

        virtual bool DropItem(UserItem *item);

        virtual bool DropGold(unsigned int gold);

    public:
        void Process() override;

        void SetOperateTime() override;

        void Process(DelayedAction *action) override;

        void PetRecall();
    protected:
        virtual void CompleteAttack(std::vector<std::any> &data);

        virtual void CompleteRangeAttack(std::vector<std::any> &data);

        virtual void CompleteDeath(std::vector<std::any> &data);

        virtual void ProcessRegen();
        virtual void ProcessPoison();

    private:
        bool ProcessDelayedExplosion(Poison *poison);


        void ProcessBuffs();
    protected:
        virtual void ProcessAI();

        virtual void CheckAlone();

        virtual void ProcessStacking();

        virtual void ProcessSearch();

        virtual void ProcessRoam();

        virtual void ProcessTarget();

        virtual bool InAttackRange();

        virtual void FindTarget();

        virtual bool ProcessRoute();

        virtual void MoveTo(Point *location);

    public:
        virtual void Turn(MirDirection dir);

        virtual bool Walk(MirDirection dir);
    protected:
        virtual void Attack();

    public:
        void ReleaseBindingShot();

        bool FindNearby(int distance);
        bool FindFriendsNearby(int distance);

        std::vector<MapObject*> FindAllNearby(int dist, Point *location, bool needSight = true);

    protected:
        std::vector<MapObject*> FindAllTargets(int dist, Point *location, bool needSight = true);

    public:
        bool IsAttackTarget(PlayerObject *attacker) override;
        bool IsAttackTarget(MonsterObject *attacker) override;
        bool IsFriendlyTarget(PlayerObject *ally) override;

        bool IsFriendlyTarget(MonsterObject *ally) override;

        int Attacked(PlayerObject *attacker, int damage, DefenceType type = DefenceType::ACAgility, bool damageWeapon = true) override;
        int Attacked(MonsterObject *attacker, int damage, DefenceType type = DefenceType::ACAgility) override;

        int Struck(int damage, DefenceType type = DefenceType::ACAgility) override;

        void ApplyPoison(Poison *p, MapObject *Caster = nullptr, bool NoResist = false, bool ignoreDefence = true) override;

        Buff *AddBuff(BuffType type, MapObject *owner, int duration, Stats *stats, bool visible = false, bool infinite = false, bool stackable = false, bool refreshStats = true, std::vector<int> &values) override;

        Packet *GetInfo() override;

        void ReceiveChat(const std::string &text, ChatType type) override;

        void RemoveObjects(MirDirection dir, int count);
        void AddObjects(MirDirection dir, int count);

        void Add(PlayerObject *player) override;

        void SendHealth(PlayerObject *player) override;

        void PetExp(unsigned int amount);
        void Despawn() override;


        // MONSTER AI ATTACKS \\\
    protected:
        virtual void PoisonTarget(MapObject *target, int chanceToPoison, long long poisonDuration, PoisonType poison, long long poisonTickSpeed = 1000);

        virtual void TriangleAttack(int distance, int limitWidth = -1, int additionalDelay = 500, DefenceType defenceType = DefenceType::ACAgility, bool push = false);

        virtual void LineAttack(int distance, int additionalDelay = 500, DefenceType defenceType = DefenceType::ACAgility, bool push = false);

        virtual void HalfmoonAttack(int delay = 500, DefenceType defenceType = DefenceType::ACAgility);

        virtual void FullmoonAttack(int delay = 500, DefenceType defenceType = DefenceType::ACAgility);

        virtual void ProjectileAttack(int minAttackStat, int maxAttackStat, DefenceType type = DefenceType::ACAgility, int additionalDelay = 500);

        virtual void SinglePushAttack(int minAttackStat, int MaxAttackStat, DefenceType type = DefenceType::AC, int delay = 500, int pushDistance = 3);
    };
}
